package net.seninp.grammarviz.view; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.geom.Rectangle2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.swing.border.TitledBorder; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartPanel; import org.jfree.chart.ChartUtilities; import org.jfree.chart.JFreeChart; import org.jfree.chart.annotations.XYTextAnnotation; import org.jfree.chart.axis.AxisState; import org.jfree.chart.axis.NumberAxis; import org.jfree.chart.axis.NumberTick; import org.jfree.chart.axis.TickType; import org.jfree.chart.event.ChartProgressEvent; import org.jfree.chart.event.ChartProgressListener; import org.jfree.chart.plot.IntervalMarker; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.plot.ValueMarker; import org.jfree.chart.plot.XYPlot; import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; import org.jfree.data.Range; import org.jfree.data.statistics.HistogramDataset; import org.jfree.data.statistics.HistogramType; import org.jfree.data.xy.XYSeries; import org.jfree.data.xy.XYSeriesCollection; import org.jfree.ui.Layer; import org.jfree.ui.LengthAdjustmentType; import org.jfree.ui.RectangleAnchor; import org.jfree.ui.RectangleEdge; import org.jfree.ui.TextAnchor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import net.seninp.gi.logic.GrammarRuleRecord; import net.seninp.gi.logic.RuleInterval; import net.seninp.grammarviz.logic.CoverageCountStrategy; import net.seninp.grammarviz.session.UserSession; import net.seninp.jmotif.sax.discord.DiscordRecord; /** * * Handles the chart panel and listens for events from Sequitur rules table. * * @author Manfred Lerner, seninp * */ public class GrammarvizChartPanel extends JPanel implements PropertyChangeListener, ChartProgressListener, ActionListener { /** Fancy serial. */ private static final long serialVersionUID = -2710973854572981568L; // various display string constants // private static final String LABEL_DEFAULT = " Data display "; private static final String LABEL_SHOWING_RULES = " Data display: showing rule subsequences "; private static final String LABEL_SHOWING_HISTOGRAMM = " Data display: showing rules length histogramm "; private static final String LABEL_SHOWING_DENSITY = " Data display: showing grammar rules density, "; private static final String LABEL_SHOWING_PACKED_RULES = " Data display: showing packed rule subsequences "; private static final String LABEL_SHOWING_PERIODS = " Data display: showing periods between selected rules "; private static final String LABEL_SHOWING_ANOMALY = " Data display: showing anomaly "; private static final String LABEL_SAVING_CHART = " Data display: saving the rules density chart "; private static final String LABEL_SELECT_INTERVAL = " Select the timeseries interval for guessing "; public static final String SELECTION_CANCELLED = "interval_selection_cancelled"; public static final String SAMPLING_SUCCEEDED = "parameters_sampling_succeeded"; public static final String SELECTION_FINISHED = "interval_selection_finished"; /** Current chart data instance. */ protected double[] tsData; /** The chart container. */ private JFreeChart chart; /** The timeseries plot itself. */ private XYPlot timeseriesPlot; /** JFreeChart Object holding the chart times series */ XYSeriesCollection chartXYSeriesCollection; /** Position of the previous mouse click in the chart */ double previousClickPosition = 0; /** The user session var - holds all parameters. */ protected UserSession session; /** The inner panel which displays the chart in place. */ private ChartPanel chartPanel; private Thread guessRefreshThread; private JButton setOperationalButton; private ArrayList<ActionListener> listeners = new ArrayList<ActionListener>(); // static block - we instantiate the logger // private static final Logger LOGGER = LoggerFactory.getLogger(GrammarRulesPanel.class); /** * Constructor. */ public GrammarvizChartPanel() { super(); } /** * This sets the chartData and forces the panel to repaint itself showing the new chart. * * @param chartData the data to use. */ public void setSession(UserSession session) { this.session = session; this.resetChartPanel(); } /** * Get the chart. * * @return JFreeChart object */ public JFreeChart getChart() { return chart; } /** * Creates the chart panel, puts it on display. * */ public void resetChartPanel() { // this is the new "insert" - elastic boundaries chart panel // if (null == this.session.chartData && null != this.tsData) { paintTheChart(this.tsData); } else { paintTheChart(this.session.chartData.getOriginalTimeseries()); } // reset the border label // TitledBorder tb = (TitledBorder) this.getBorder(); tb.setTitle(LABEL_DEFAULT); // instantiate and adjust the chart panel // chartPanel = new ChartPanel(this.chart); chartPanel.setMaximumDrawHeight(this.getParent().getHeight()); chartPanel.setMaximumDrawWidth(this.getParent().getWidth()); chartPanel.setMinimumDrawWidth(0); chartPanel.setMinimumDrawHeight(0); chartPanel.setMouseWheelEnabled(true); // cleanup all the content // this.removeAll(); // put the chart on show // this.add(chartPanel); // make sure all is set // this.revalidate(); } /** * Highlights the subsequence of the rule. * * @param The rule index. */ private void highlightPatternInChart(ArrayList<String> rules) { LOGGER.debug("Selected rules: " + rules.toString()); timeseriesPlot.clearDomainMarkers(); for (String rule : rules) { int ruleId = Integer.valueOf(rule); if (0 == ruleId) { continue; } ArrayList<RuleInterval> arrPos = this.session.chartData.getRulePositionsByRuleNum(ruleId); LOGGER.debug("Size: " + arrPos.size() + " - Positions: " + arrPos); for (RuleInterval saxPos : arrPos) { addMarker(timeseriesPlot, saxPos.getStart(), saxPos.getEnd()); } } } /** * Highlights the subsequence of the rule. * * @param The rule index. */ private void highlightPatternInChartPacked(ArrayList<String> rules) { LOGGER.debug("Selected class: " + rules.toString()); timeseriesPlot.clearDomainMarkers(); for (String rule : rules) { int ruleId = Integer.valueOf(rule); // if (0 == ruleId) { // continue; // } ArrayList<RuleInterval> arrPos = this.session.chartData .getSubsequencesPositionsByClassNum(Integer.valueOf(ruleId)); LOGGER.debug("Size: " + arrPos.size() + " - Positions: " + arrPos); for (RuleInterval saxPos : arrPos) { addMarker(timeseriesPlot, saxPos.getStart(), saxPos.getEnd()); } } } /** * Highlights intervals in between selected rule subsequences - ones which suppose to be periods. * * @param rule The rule whose subsequences will be period boundaries. */ private void highlightPeriodsBetweenPatterns(String rule) { LOGGER.debug("Selected rule: " + rule); ArrayList<RuleInterval> arrPos = this.session.chartData .getRulePositionsByRuleNum(Integer.valueOf(rule)); LOGGER.debug("Size: " + arrPos.size() + " - Positions: " + arrPos); timeseriesPlot.clearDomainMarkers(); for (int i = 1; i < arrPos.size(); i++) { RuleInterval c = arrPos.get(i - 1); RuleInterval p = arrPos.get(i); addPeriodMarker(timeseriesPlot, c.getEnd(), p.getStart()); } } private void highlightAnomaly(ArrayList<String> anomalies) { LOGGER.debug("Selected anomalies: " + anomalies.toString()); timeseriesPlot.clearDomainMarkers(); for (String anomaly : anomalies) { DiscordRecord dr = this.session.chartData.getAnomalies().get(Integer.valueOf(anomaly)); LOGGER.debug(dr.toString()); addAnomalyMarker(timeseriesPlot, dr.getPosition(), dr.getPosition() + dr.getLength()); } } /** * Puts rules density on show. */ private void displayRuleDensity() { // this is the new "insert" - elastic boundaries chart panel // // paintTheChart(this.session.chartData.getOriginalTimeseries()); // chartPanel = new ChartPanel(this.chart); // chartPanel.setMaximumDrawHeight(this.getParent().getHeight()); // chartPanel.setMaximumDrawWidth(this.getParent().getWidth()); // chartPanel.setMinimumDrawWidth(0); // chartPanel.setMinimumDrawHeight(0); // chartPanel.revalidate(); // this.removeAll(); this.add(chartPanel); // init vars // int maxObservedCoverage = Integer.MIN_VALUE; int minObservedCoverage = Integer.MAX_VALUE; int[] coverageArray = new int[this.session.chartData.getOriginalTimeseries().length]; for (GrammarRuleRecord r : this.session.chartData.getGrammarRules()) { if (0 == r.ruleNumber()) { // skip R0 continue; } ArrayList<RuleInterval> occurrences = this.session.chartData .getRulePositionsByRuleNum(r.ruleNumber()); for (RuleInterval i : occurrences) { int start = i.getStart(); int end = i.getEnd(); for (int j = start; j < end; j++) { if (CoverageCountStrategy.COUNT.equals(this.session.countStrategy)) { coverageArray[j] = coverageArray[j] + 1; } else if (CoverageCountStrategy.LEVEL.equals(this.session.countStrategy)) { coverageArray[j] = coverageArray[j] + r.getRuleLevel(); } else if (CoverageCountStrategy.OCCURRENCE.equals(this.session.countStrategy)) { coverageArray[j] = coverageArray[j] + r.getOccurrences().size(); } else if (CoverageCountStrategy.YIELD.equals(this.session.countStrategy)) { coverageArray[j] = coverageArray[j] + r.getRuleYield(); } else if (CoverageCountStrategy.PRODUCT.equals(this.session.countStrategy)) { coverageArray[j] = coverageArray[j] + r.getRuleLevel() * r.getOccurrences().size(); } if (maxObservedCoverage < coverageArray[j]) { maxObservedCoverage = coverageArray[j]; } if (minObservedCoverage > coverageArray[j]) { minObservedCoverage = coverageArray[j]; } } } } // since we know the maximal coverage value, we can compute the increment for a single coverage // interval double covIncrement = 1.0 / (double) maxObservedCoverage; for (GrammarRuleRecord r : this.session.chartData.getGrammarRules()) { if (0 == r.ruleNumber()) { // skip the R0 continue; } ArrayList<RuleInterval> occurrences = r.getRuleIntervals(); for (RuleInterval i : occurrences) { IntervalMarker marker = new IntervalMarker(i.getStart(), i.getEnd()); marker.setLabelOffsetType(LengthAdjustmentType.EXPAND); marker.setPaint(Color.BLUE); // marker.setAlpha((float) 0.05); if (CoverageCountStrategy.COUNT.equals(this.session.countStrategy)) { marker.setAlpha((float) covIncrement); } else if (CoverageCountStrategy.LEVEL.equals(this.session.countStrategy)) { marker.setAlpha((float) covIncrement * r.getRuleLevel()); } else if (CoverageCountStrategy.OCCURRENCE.equals(this.session.countStrategy)) { marker.setAlpha((float) covIncrement * r.getOccurrences().size()); } else if (CoverageCountStrategy.YIELD.equals(this.session.countStrategy)) { marker.setAlpha((float) covIncrement * r.getRuleYield()); } else if (CoverageCountStrategy.PRODUCT.equals(this.session.countStrategy)) { marker.setAlpha((float) covIncrement * (r.getRuleLevel() * r.getOccurrences().size())); } marker.setLabelFont(new Font("SansSerif", Font.PLAIN, 12)); marker.setLabelPaint(Color.green); marker.setLabelAnchor(RectangleAnchor.TOP_LEFT); marker.setLabelTextAnchor(TextAnchor.TOP_LEFT); timeseriesPlot.addDomainMarker(marker, Layer.BACKGROUND); } } int sum = 0; for (int d : coverageArray) sum += d; double meanCoverage = 1.0d * sum / coverageArray.length; DecimalFormat df = new DecimalFormat("#.00"); String annotationString = "min C:" + minObservedCoverage + ", max C:" + maxObservedCoverage + ", mean C:" + df.format(meanCoverage); NumberAxis domain = (NumberAxis) this.timeseriesPlot.getDomainAxis(); Range domainRange = domain.getRange(); NumberAxis range = (NumberAxis) this.timeseriesPlot.getRangeAxis(); Range rangeRange = range.getRange(); XYTextAnnotation a = new XYTextAnnotation(annotationString, domainRange.getLowerBound() + domainRange.getLength() / 100, rangeRange.getLowerBound() + 0.5); a.setTextAnchor(TextAnchor.BOTTOM_LEFT); a.setPaint(Color.RED); a.setOutlinePaint(Color.BLACK); a.setOutlineVisible(true); a.setFont(new java.awt.Font("SansSerif", java.awt.Font.BOLD, 14)); this.timeseriesPlot.addAnnotation(a); // not sure if I need this // revalidate(); repaint(); // and finally save the coverage curve // this.saveRuleDensityCurve(coverageArray); } private void saveRuleDensityCurve(int[] coverageArray) { // write down the coverage array // try { String filename = session.ruleDensityOutputFileName; BufferedWriter bw = new BufferedWriter(new FileWriter(new File(filename))); for (int c : coverageArray) { bw.write(String.valueOf(c) + "\n"); } bw.close(); } catch (IOException e) { e.printStackTrace(); } } private void displayRulesLengthHistogram() { // cleanup all the content // this.removeAll(); revalidate(); repaint(); // construct the dataset // // [1.0] extract all the rules ArrayList<Integer> allRules = new ArrayList<Integer>(); for (GrammarRuleRecord r : this.session.chartData.getGrammarRules()) { if (0 == r.ruleNumber()) { continue; } for (RuleInterval interval : r.getRuleIntervals()) { allRules.add(interval.getLength()); } } // [2.0] make data Collections.sort(allRules); // final int minLength = allRules.get(0); final int maxLength = allRules.get(allRules.size() - 1); final int numberOfBins = maxLength / this.session.chartData.getSAXWindowSize() + 1; double[] values = new double[allRules.size()]; for (int i = 0; i < allRules.size(); i++) { values[i] = allRules.get(i).doubleValue(); } HistogramDataset dataset = new HistogramDataset(); dataset.setType(HistogramType.FREQUENCY); dataset.addSeries("Frequencies", values, numberOfBins, 0, numberOfBins * this.session.chartData.getSAXWindowSize()); String plotTitle = "Rules Length Histogram"; String xaxis = "Rule length"; String yaxis = "Counts"; PlotOrientation orientation = PlotOrientation.VERTICAL; boolean show = true; boolean toolTips = false; boolean urls = false; this.chart = ChartFactory.createHistogram(plotTitle, xaxis, yaxis, dataset, orientation, show, toolTips, urls); this.chart.removeLegend(); NumberAxis myAxis = new NumberAxis(this.chart.getXYPlot().getDomainAxis().getLabel()) { private static final long serialVersionUID = 5839368758428973857L; @Override public List<NumberTick> refreshTicks(Graphics2D g2, AxisState state, Rectangle2D dataArea, RectangleEdge edge) { // List<NumberTick> allTicks = super.refreshTicks(g2, state, dataArea, edge); List<NumberTick> myTicks = new ArrayList<NumberTick>(); for (int i = 0; i < numberOfBins; i++) { myTicks.add(new NumberTick(TickType.MAJOR, i * session.chartData.getSAXWindowSize(), String.valueOf(i * session.chartData.getSAXWindowSize()), TextAnchor.CENTER, TextAnchor.CENTER, 0.0d)); // textAnchor, rotationAnchor, angle)); } // for (Object tick : allTicks) { // NumberTick numberTick = (NumberTick) tick; // // if (TickType.MAJOR.equals(numberTick.getTickType()) // && (numberTick.getValue() % this.session.chartData.getSAXWindowSize() != 0)) { // // myTicks.add(new NumberTick(TickType.MINOR, numberTick.getValue(), "", numberTick // // .getTextAnchor(), numberTick.getRotationAnchor(), numberTick.getAngle())); // continue; // } // myTicks.add(tick); // } return myTicks; } }; this.chart.getXYPlot().setDomainAxis(myAxis); chartPanel = new ChartPanel(this.chart); chartPanel.setMinimumDrawWidth(0); chartPanel.setMinimumDrawHeight(0); chartPanel.setMaximumDrawWidth(1920); chartPanel.setMaximumDrawHeight(1200); // cleanup all the content // this.removeAll(); // put the chart on show // this.add(chartPanel); revalidate(); repaint(); } /** * @param plot plot for the marker * @param startVal start postion * @param endVal end position */ protected void addMarker(XYPlot plot, int startVal, int endVal) { IntervalMarker marker = new IntervalMarker(startVal, endVal); marker.setLabelOffsetType(LengthAdjustmentType.EXPAND); marker.setPaint(new Color(134, 254, 225)); marker.setAlpha((float) 0.60); marker.setLabelFont(new Font("SansSerif", Font.PLAIN, 12)); marker.setLabelPaint(Color.green); marker.setLabelAnchor(RectangleAnchor.TOP_LEFT); marker.setLabelTextAnchor(TextAnchor.TOP_LEFT); plot.addDomainMarker(marker, Layer.BACKGROUND); ValueMarker markStart = new ValueMarker(startVal, new Color(31, 254, 225), new BasicStroke(2.0f)); ValueMarker markEnd = new ValueMarker(endVal, new Color(31, 254, 225), new BasicStroke(2.0f)); plot.addDomainMarker(markStart, Layer.BACKGROUND); plot.addDomainMarker(markEnd, Layer.BACKGROUND); } /** * Adds a periodicity marker. * * @param plot plot for the marker * @param startVal start postion * @param endVal end position */ protected void addPeriodMarker(XYPlot plot, int startVal, int endVal) { IntervalMarker marker = new IntervalMarker(startVal, endVal); marker.setLabelOffsetType(LengthAdjustmentType.EXPAND); marker.setPaint(new Color(134, 254, 225)); marker.setAlpha((float) 0.60); marker.setLabelFont(new Font("SansSerif", Font.PLAIN, 12)); marker.setLabelPaint(Color.blue); marker.setLabelAnchor(RectangleAnchor.TOP_LEFT); marker.setLabelTextAnchor(TextAnchor.TOP_LEFT); marker.setPaint(Color.blue); plot.addDomainMarker(marker, Layer.BACKGROUND); } /** * Adds an anomaly marker. * * @param plot plot for the marker * @param startVal start postion * @param endVal end position */ protected void addAnomalyMarker(XYPlot plot, int startVal, int endVal) { IntervalMarker marker = new IntervalMarker(startVal, endVal); marker.setLabelOffsetType(LengthAdjustmentType.EXPAND); marker.setPaint(new Color(134, 254, 225)); marker.setAlpha((float) 0.60); marker.setLabelFont(new Font("SansSerif", Font.PLAIN, 12)); marker.setLabelPaint(Color.pink); marker.setLabelAnchor(RectangleAnchor.TOP_LEFT); marker.setLabelTextAnchor(TextAnchor.TOP_LEFT); marker.setPaint(Color.pink); plot.addDomainMarker(marker, Layer.BACKGROUND); } /** * Plot the timeseries at the panel. * * @param tsData The time series data. */ public void showTimeSeries(double[] tsData) { this.tsData = tsData; paintTheChart(tsData); chartPanel = new ChartPanel(this.chart); this.removeAll(); this.add(chartPanel); revalidate(); repaint(); } /** * create the chart for the original time series * * @param tsData the data to plot. * * @return a JFreeChart object of the chart */ private void paintTheChart(double[] tsData) { // making the data // XYSeries dataset = new XYSeries("Series"); for (int i = 0; i < tsData.length; i++) { dataset.add(i, (float) tsData[i]); } chartXYSeriesCollection = new XYSeriesCollection(dataset); // set the renderer // XYLineAndShapeRenderer xyRenderer = new XYLineAndShapeRenderer(true, false); xyRenderer.setSeriesPaint(0, new Color(0, 0, 0)); xyRenderer.setBaseStroke(new BasicStroke(3)); // X - the time axis // NumberAxis timeAxis = new NumberAxis("Time. (zoom: select with mouse; panning: Ctrl+mouse)"); // Y axis // NumberAxis valueAxis = new NumberAxis("Values"); valueAxis.setAutoRangeIncludesZero(false); // put these into collection of dots // this.timeseriesPlot = new XYPlot(chartXYSeriesCollection, timeAxis, valueAxis, xyRenderer); // enabling panning // this.timeseriesPlot.setDomainPannable(true); this.timeseriesPlot.setRangePannable(true); // finally, create the chart this.chart = new JFreeChart("", JFreeChart.DEFAULT_TITLE_FONT, timeseriesPlot, false); // set the progress listener to react to mouse clicks in the chart this.chart.addProgressListener(this); this.chart.setNotify(true); } public void chartProgress(ChartProgressEvent chartprogressevent) { if (chartprogressevent.getType() != 2) return; XYPlot xyplot = (XYPlot) chart.getPlot(); double pos = xyplot.getDomainCrosshairValue(); // this is needed because the call of highlightPatternInChart triggers a ChartProgessEvent if (previousClickPosition == pos) { return; } // SAXString sax = new SAXString(this.session.chartData.getFreqData(), " "); // String rule = sax.getRuleFromPosition(this.session.chartData, (int) pos); // if (rule != null) { // firePropertyChange(SequiturMessage.MAIN_CHART_CLICKED_MESSAGE, "", rule); // System.out.println("Clicked Property Change fired with rule: " + rule); // } previousClickPosition = pos; } @Override public void propertyChange(PropertyChangeEvent evt) { if (GrammarRulesPanel.FIRING_PROPERTY.equalsIgnoreCase(evt.getPropertyName())) { @SuppressWarnings("unchecked") ArrayList<String> newlySelectedRows = (ArrayList<String>) evt.getNewValue(); highlightPatternInChart(newlySelectedRows); TitledBorder tb = (TitledBorder) this.getBorder(); tb.setTitle(LABEL_SHOWING_RULES); revalidate(); repaint(); } if (PackedRulesPanel.FIRING_PROPERTY_PACKED.equalsIgnoreCase(evt.getPropertyName())) { @SuppressWarnings("unchecked") ArrayList<String> newlySelectedRows = (ArrayList<String>) evt.getNewValue(); // String newlySelectedRaw = (String) evt.getNewValue(); highlightPatternInChartPacked(newlySelectedRows); TitledBorder tb = (TitledBorder) this.getBorder(); tb.setTitle(LABEL_SHOWING_PACKED_RULES); revalidate(); repaint(); } if (RulesPeriodicityPanel.FIRING_PROPERTY_PERIOD.equalsIgnoreCase(evt.getPropertyName())) { String newlySelectedRaw = (String) evt.getNewValue(); highlightPeriodsBetweenPatterns(newlySelectedRaw); TitledBorder tb = (TitledBorder) this.getBorder(); tb.setTitle(LABEL_SHOWING_PERIODS); revalidate(); repaint(); } if (GrammarVizAnomaliesPanel.FIRING_PROPERTY_ANOMALY.equalsIgnoreCase(evt.getPropertyName())) { @SuppressWarnings("unchecked") ArrayList<String> newlySelectedRaws = (ArrayList<String>) evt.getNewValue(); highlightAnomaly(newlySelectedRaws); TitledBorder tb = (TitledBorder) this.getBorder(); tb.setTitle(LABEL_SHOWING_ANOMALY); revalidate(); repaint(); } } @Override public void actionPerformed(ActionEvent e) { if (GrammarVizView.DISPLAY_DENSITY_DATA.equalsIgnoreCase(e.getActionCommand())) { TitledBorder tb = (TitledBorder) this.getBorder(); tb.setTitle(LABEL_SHOWING_DENSITY + "coverage count strategy: " + this.session.countStrategy.toString() + " "); revalidate(); repaint(); displayRuleDensity(); } else if (GrammarVizView.DISPLAY_LENGTH_HISTOGRAM.equalsIgnoreCase(e.getActionCommand())) { TitledBorder tb = (TitledBorder) this.getBorder(); tb.setTitle(LABEL_SHOWING_HISTOGRAMM); revalidate(); displayRulesLengthHistogram(); } else if (GrammarVizView.SAVE_CHART.equalsIgnoreCase(e.getActionCommand())) { TitledBorder tb = (TitledBorder) this.getBorder(); tb.setTitle(LABEL_SAVING_CHART); revalidate(); repaint(); saveCurrentChart(); } // GUESS PARAMETERS procedure // else if (GrammarVizView.GUESS_PARAMETERS.equalsIgnoreCase(e.getActionCommand())) { LOGGER.info("Starting the sampling dialog..."); // re-draw the plot, so selection wouldn't get weird... // this.resetChartPanel(); // setting the new label on the chart-surrounding panel // TitledBorder tb = (TitledBorder) this.getBorder(); tb.setTitle(LABEL_SELECT_INTERVAL); // clear whatever markers are on the chart panel // timeseriesPlot.clearDomainMarkers(); // disabling zoom on the panel // chartPanel.setRangeZoomable(false); chartPanel.setDomainZoomable(false); JOptionPane.showMessageDialog(this, "Select the sampling range (preferrably the normal signal)\n" + "by dragging the mouse pointer from left to right.", null, JOptionPane.WARNING_MESSAGE); // attaching the custom mouse listener // final MouseMarker listener = new MouseMarker(chartPanel); chartPanel.addMouseListener(listener); chartPanel.addMouseMotionListener(listener); // creating the sampler object // final GrammarvizParamsSampler paramsSampler = new GrammarvizParamsSampler(this); // running the thread which will look over the selection // final Object selectionLock = new Object(); final JFrame topFrame = (JFrame) SwingUtilities.getWindowAncestor(this); listener.setLockObject(selectionLock); guessRefreshThread = new Thread(new Runnable() { public void run() { boolean selectionSucceeded = false; while (!(selectionSucceeded)) { synchronized (selectionLock) { try { selectionLock.wait(); // Send this thread to sleep until dirtyLock is unlocked } catch (InterruptedException e1) { } } session.samplingStart = (int) Math.floor(listener.getSelectionStart()); session.samplingEnd = (int) Math.ceil(listener.getSelectionEnd()); GrammarvizGuesserPane parametersPanel = new GrammarvizGuesserPane(session); GrammarvizGuesserDialog parametersDialog = new GrammarvizGuesserDialog(topFrame, parametersPanel, session); parametersDialog.setVisible(true); if (parametersDialog.wasCancelled) { LOGGER.info("Selection process has been cancelled..."); paramsSampler.cancel(); } else { selectionSucceeded = true; } } if (selectionSucceeded) { LOGGER.info("Running the sampler..."); try { final ExecutorService executorService = Executors.newSingleThreadExecutor(); final Future<String> bestParams = executorService.submit(paramsSampler); setOperationalButton.setText("Stop!"); setOperationalButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { bestParams.cancel(true); executorService.shutdown(); } }); setOperationalButton.revalidate(); setOperationalButton.repaint(); if (!executorService.awaitTermination(10, TimeUnit.MINUTES)) { executorService.shutdownNow(); // Cancel currently executing tasks if (!executorService.awaitTermination(30, TimeUnit.SECONDS)) { System.err.println("Pool did not terminate... FATAL ERROR"); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }); guessRefreshThread.start(); } else if (GrammarvizChartPanel.SELECTION_CANCELLED.equalsIgnoreCase(e.getActionCommand())) { LOGGER.info("selection cancelled..."); this.resetChartPanel(); ActionEvent event = new ActionEvent(this, 0, GrammarVizView.RESET_GUESS_BUTTON_LISTENER); for (ActionListener l : this.listeners) { l.actionPerformed(event); } } else if (GrammarvizChartPanel.SELECTION_FINISHED.equalsIgnoreCase(e.getActionCommand())) { LOGGER.info("selection finished..."); this.resetChartPanel(); ActionEvent event = new ActionEvent(this, 0, GrammarVizView.RESET_GUESS_BUTTON_LISTENER); for (ActionListener l : this.listeners) { l.actionPerformed(event); } } else if (GrammarvizChartPanel.SAMPLING_SUCCEEDED.equalsIgnoreCase(e.getActionCommand())) { resetChartPanel(); this.session.notifyParametersChangeListeners(); ActionEvent event = new ActionEvent(this, 0, GrammarVizView.RESET_GUESS_BUTTON_LISTENER); for (ActionListener l : this.listeners) { l.actionPerformed(event); } } } /** * Quick and dirty hack for saving the current chart -- because normally the chart parameters need * to be defined and modifiable by the user. */ private void saveCurrentChart() { String fileName = new SimpleDateFormat("yyyyMMddhhmmssSS'.png'").format(new Date()); try { NumberAxis domain = (NumberAxis) this.timeseriesPlot.getDomainAxis(); Range domainRange = domain.getRange(); NumberAxis range = (NumberAxis) this.timeseriesPlot.getRangeAxis(); Range rangeRange = range.getRange(); String annotationString = "W:" + this.session.chartData.getSAXWindowSize() + ", P:" + this.session.chartData.getSAXPaaSize() + ", A:" + this.session.chartData.getSAXAlphabetSize(); XYTextAnnotation a = new XYTextAnnotation(annotationString, domainRange.getLowerBound() + domainRange.getLength() / 100, rangeRange.getLowerBound() + rangeRange.getLength() / 5 * 3.5); a.setTextAnchor(TextAnchor.BOTTOM_LEFT); a.setPaint(Color.RED); a.setOutlinePaint(Color.BLACK); a.setOutlineVisible(true); a.setFont(new java.awt.Font("SansSerif", java.awt.Font.BOLD, 14)); // XYPointerAnnotation a = new XYPointerAnnotation("Bam!", domainRange.getLowerBound() // + domainRange.getLength() / 10, rangeRange.getLowerBound() + rangeRange.getLength() / 5 // * 4, 5 * Math.PI / 8); this.timeseriesPlot.addAnnotation(a); // this.paintTheChart(); ChartUtilities.saveChartAsPNG(new File(fileName), this.chart, 900, 600); } catch (IOException e) { e.printStackTrace(); } } public void setOperationalButton(JButton guessParametersButton) { this.setOperationalButton = guessParametersButton; } public void addActionListener(ActionListener listener) { this.listeners.add(listener); } // working around the scaling chart issue // public void bindToTheFrameSize() { this.getTopLevelAncestor().addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { if (null != chartPanel) { // System.err.println("component resized"); chartPanel.setMaximumDrawHeight(e.getComponent().getHeight()); chartPanel.setMaximumDrawWidth(e.getComponent().getWidth()); chartPanel.setMinimumDrawWidth(0); chartPanel.setMinimumDrawHeight(0); chartPanel.revalidate(); } } }); } }